class Cargo 
{ 

};

function Cargo::BuildCargoRoute(indStart, indEnd, cargoType)
{
    //TODO: keep it from building depots that face other depots (possibly stations too?)
    //Log.Info("indStart = " + indStart + "; indEnd = " + indEnd + "; cargoType = " + cargoType, Log.LVL_DEBUG);
    
    //Find a place for the source station
    AIRoad.SetCurrentRoadType(AIRoad.ROADTYPE_ROAD);
    
    local startStationTile = -1;
    local endStationTile = -1;
    local startDepotTile = -1;
    local endDepotTile = -1;
    
    startStationTile = Road.BuildStopForIndustry(indStart, cargoType); //build starting station via superlib
    endStationTile = Road.BuildStopForIndustry(indEnd, cargoType); //build ending station via superlib
    
    if (!startStationTile || !endStationTile)
    {
        Log.Error("Either the start or ending station did not get built. Aborting.", Log.LVL_INFO);
        return -1;
    }       
        
    local builder = RoadBuilder();
    
    builder.Init(startStationTile, endStationTile);
    local pathResult = builder.ConnectTiles();
    
    if (pathResult != 0)
    {
        Log.Error("Unable to connect stations. Aborting and removing stations.", Log.LVL_INFO);
        Station.DemolishStation(AIStation.GetStationID(startStationTile));
        Station.DemolishStation(AIStation.GetStationID(endStationTile));
        return -1
    }       
    
    //build depots
    startDepotTile = Road.BuildDepotNextToRoad(startStationTile, 1, 500);
    endDepotTile = Road.BuildDepotNextToRoad(endStationTile, 1, 500);
    
    if (!startDepotTile || !endDepotTile)
    {
        Log.Error("Either the start or ending depot did not get built. Aborting.", Log.LVL_INFO);
        return -1;
    }       

    //Now that the path is built, let's build some vehicles.
    local useDepot = null;
    
    if (AIRoad.IsRoadDepotTile(startDepotTile)) //in case one of the depots didn't get built
    {
        useDepot = startDepotTile;
    }
    else
    {
        useDepot = endDepotTile;
    }      

    //We need to figure out which type of bus to buy.
    
    local vehList = AIEngineList(AIVehicle.VT_ROAD);
    local mustRefit = false;
    
    //only the buildable engines
    vehList.Valuate(AIEngine.IsBuildable);
    vehList.KeepValue(1);

    //and only road buses, not trams
    vehList.Valuate(AIEngine.GetRoadType);
    vehList.KeepValue(AIRoad.ROADTYPE_ROAD);
    
    //now, only those suitable for this cargo
    vehList.Valuate(AIEngine.GetCargoType);
    vehList.KeepValue(cargoType);
    
    //but not articulated -- we're using pull in stations
    vehList.Valuate(AIEngine.IsArticulated);
    vehList.KeepValue(0);
    
    if (vehList.IsEmpty()) //No vehicles for this cargo?? Maybe we can refit one!
    {
        vehList = AIEngineList(AIVehicle.VT_ROAD); //repopulate
        
        //only the buildable engines
    vehList.Valuate(AIEngine.IsBuildable);
    vehList.KeepValue(1);
        
        //and only road buses, not trams
        vehList.Valuate(AIEngine.GetRoadType);
        vehList.KeepValue(AIRoad.ROADTYPE_ROAD);
    
    //See if we can refit one of the truck types
    vehList.Valuate(AIEngine.CanRefitCargo, cargoType);
    vehList.KeepValue(1);
    
    //but not articulated -- we're using pull in stations
    vehList.Valuate(AIEngine.IsArticulated);
    vehList.KeepValue(0);
    
    //Do we have a winner?
    if (!vehList.IsEmpty())
    {
       mustRefit = true;
    }
    else
    {
       Log.Warning("No vehicles available for this type of route. Aborting.", Log.LVL_INFO);
       return -1;
    }   

    }
    
    //we want the fastest type of vehicle.
    vehList.Valuate(AIEngine.GetMaxSpeed);
    vehList.Sort(AIAbstractList.SORT_BY_VALUE, AIAbstractList.SORT_DESCENDING);
    
    local vehType = vehList.Begin(); //we'll build the one at the top of the list.
    local vehs = [];
    
    vehs.insert(0, AIVehicle.BuildVehicle(useDepot, vehType)); //build one truck of the right type in the desired depot
    
    if (!AIVehicle.IsValidVehicle(vehs[0]))
    {
        local errorCode = AIError.GetLastError();
            
        if (errorCode == AIVehicle.ERR_VEHICLE_TOO_MANY)
        {
            Log.Warning("Unable to create any vehicles for route; we've reached max road vehicles.", Log.LVL_INFO)
            this.at_max_RV_count = true;
            return -1;
        }
        else
        {
            Log.Error("Unable to create vehicles for route: unhandled error: " + AIError.GetLastErrorString(), Log.LVL_INFO);
            return -1;
        }
    }
    
    //refit if necessary
    if (mustRefit)
    {
        Log.Info("Refitting.", Log.LVL_DEBUG);
        AIVehicle.RefitVehicle(vehs[0], cargoType);
    }       
    
    //give this truck orders     
    AIOrder.AppendOrder(vehs[0], startStationTile, AIOrder.AIOF_FULL_LOAD_ANY);
    AIOrder.AppendOrder(vehs[0], endStationTile, AIOrder.AIOF_UNLOAD);

    for (local x = 1; x < 3; x++) //purchase subsequent trucks
    {
        vehs.push(AIVehicle.CloneVehicle(useDepot, vehs[0], true));
    }    
           
    Log.Info("Vehicles purchased. Starting them.", Log.LVL_SUB_DECISIONS);
    
    for (local y = 0; y < 3; y++)
    {
        AIVehicle.StartStopVehicle(vehs[y]);
        TeshiNet.Sleep(150);
    }
    
    //make sure we don't use these industries again
    this.industries_used.AddItem(indStart, AIIndustry.GetLocation(indStart));
    this.industries_used.AddItem(indEnd, AIIndustry.GetLocation(indEnd));
    
    this.last_route_tick = TeshiNet.GetTick(); //record time of route creation
    
    //record the depots for these stations
    if (useDepot == endDepotTile) startDepotTile = endDepotTile;
    this.station_depot_pairs.AddItem(startStationTile, startDepotTile);
    this.station_depot_pairs.AddItem(endStationTile, endDepotTile);
    
    //record this set of stations as a route (need to store it both ways for list functionality)
    this.station_pairs.AddItem(AIStation.GetStationID(startStationTile), AIStation.GetStationID(endStationTile));
    this.station_pairs.AddItem(AIStation.GetStationID(endStationTile), AIStation.GetStationID(startStationTile));
    
    Log.Info("Route complete!", Log.LVL_INFO);
    
    return 1; //stations, depots, and vehicles built, and buses have started.
}

function Cargo::ManageBusyTruckStations()
{ 
    local masterList = AIList();
    local cargList = AICargoList();
    
    for (local cargo = cargList.Begin(); cargList.HasNext(); cargo = cargList.Next())
    {
        local stationList = AIStationList(AIStation.STATION_TRUCK_STOP); //make a list of stations
        
        stationList.Valuate(AIStation.GetCargoWaiting, cargo); //value them by cargo waiting
        stationList.KeepAboveValue(200); //let's increase if there's more than 200
        
        masterList.AddList(stationList);
    }    
            
    if (masterList.IsEmpty()) // if the list is empty, then all stations are below 200 waiting cargo, no need to increase capacity
    {
        Log.Info("No truck stations with excessive waiting cargo.", Log.LVL_SUB_DECISIONS);
        return -1;    
    }
    masterList.Sort(AIAbstractList.SORT_BY_VALUE, AIAbstractList.SORT_DESCENDING); //starting with the busiest station, since we might run out of cash
    
    for (local curStation = masterList.Begin(); masterList.HasNext(); curStation = masterList.Next())
    {      
        //calculate average profit for the route
        local vehicles = AIVehicleList_Station(curStation);

        if (vehicles.IsEmpty()) continue;

        vehicles.Valuate(AIVehicle.GetAge); //how old are they?
        vehicles.KeepAboveValue(365 * 2); //we only want to calculate on vehicles that have had two full years to run. this ensures last year's profit is a full year.

        if (vehicles.IsEmpty()) continue; //young route? give it a chance.

        vehicles.Valuate(AIVehicle.GetProfitLastYear);

        local revenuetotal = 0;

        for (local veh = vehicles.Begin(); vehicles.HasNext(); veh = vehicles.Next())
        {
            revenuetotal += vehicles.GetValue(veh);
        }    

        local meanprofit = revenuetotal / vehicles.Count(); //calculate the mean profit (total revenue divided by total vehicle count)
        local vehPrice = AIEngine.GetPrice(AIVehicle.GetEngineType(vehicles.Begin()));
                
        if ((meanprofit * 3) < vehPrice)
        {
            Log.Info("There is cargo waiting at " + AIStation.GetName(curStation) + " but a new vehicle would not pay for itself within 3 years. Skipping.", Log.LVL_SUB_DECISIONS);
            continue;
        }    
        else
        {
            Log.Info("A new vehicle for " + AIStation.GetName(curStation) + " will pay for itself in " + (vehPrice / meanprofit)+ " years.", Log.LVL_DEBUG);
        }
       
        local vehList = AIVehicleList_Station(curStation); //make a list of vehicles at this station, so we can count them
            
        local numveh = vehList.Count();
            
        local staTiles = AITileList_StationType(curStation, AIStation.STATION_TRUCK_STOP);
            
        local staSize = staTiles.Count();
                    
        if ((numveh / 15.0) > staSize) //desired ratio is 1 station tile for every 15 vehicles or fraction thereof.
        {
            Log.Info("More than 15 vehicles per area at " + AIStation.GetName(curStation) + ". Building a new loading area at each end.", Log.LVL_SUB_DECISIONS);
            Road.GrowStation(curStation, AIStation.STATION_TRUCK_STOP);
            Road.GrowStation(this.station_pairs.GetValue(curStation), AIStation.STATION_TRUCK_STOP); //the other end of this route
        }    

        CloneVehicleByStation(curStation);
    }    
}   